<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
	</head>
	<body>
		<script type="text/javascript">
			//每个函数都是 Function 类型的实例
			//函数名实际上是一个指向函数对象的指针,不会与某个函数绑定
			
			//定义函数的两种方式	
		    	//1.函数声明定义函数
						function sum(num1,num2){
						    return num1 + num2;
						}
				//2.函数表达式定义函数
						var sum = function(num1,num2){
							return num1 + num2;
						};
                //也可以同时使用函数声明和函数表达式，例如 var sum = function sum(){}。不过，这种语法在 Safari 中会导致错误。
                
                
            //函数名仅仅是指向函数的指针，因此函数名与包含对象指针的其他变量没有什么不同
            //换句话说，一个函数可能会有多个名字
            //注意，使用不带圆括号的函数名是访问函数指针，而非调用函数
		              function sum(num1,num2){
		              	 return num1 + num2;
		              }
	              	  console.log(sum(10,10));//20
	              	  var anotherSum = sum;
	                  console.log(anotherSum(10,10));//20
	                  sum = null;
	                  console.log(anotherSum(10,10));//20

             //5.5.1 没有重载
                //声明两个同名函数，后面的函数会覆盖前面的函数
                //在创建第二个函数时，实际上覆盖了引用第一个函数的变量 addSomeNumber
			             function addSomeNumber(num){
			             	return num + 100;
			             }
			             function addSomeNumber(num){
			             	return num + 200;
			             }
			             //以上代码等同于
			             var addSomeNumber = function(num){
			             	return num + 100;
			             }
			             addSomeNumber = function(num){
			             	return num + 200;
			             }
			             console.log(addSomeNumber(100));//300
             
             //5.5.2 函数声明与函数表达式
                 //解析器会率先读取函数声明，并使其在执行任何代码之前可用（可以访问）
                 //在代码开始执行之前解析器通过一个名为函数声明提升（function declaration hoisting）的过程，读取并将函数声明添加到执行环境中
		                   console.log(sum(10,10));
		                   function sum(num1,num2){
		                   	  return num1 + num2
		                   }
                //函数表达式，则必须等到解析器执行到它所在的代码行，才会真正被解释执行
		                   console.log(sum(10,10));//报错
		                   var sum = function(num1,num2){
		                   	 return num1 + num2
		                   }

             //5.5.3 作为值的函数
             	//不仅可以像传递参数一样把一个函数传递给另一个函数，而且可以将一个函数作为另一个函数的结果返回
			             function callSomeFunction(someFunction, someArgument){ 
			   				return someFunction(someArgument);
			   			 }
			             function add10(num){
			                return num + 10;
			             }
			             console.log(callSomeFunction(add10,10));//20
			             function getGreeting(name){
			             	return "Hello " + name;
			             }
			             console.log(callSomeFunction(getGreeting,"ls"));//Hello ls

		               function createComparisonFunction(propertyName){
			               	return function(obj1,obj2){
			               		var value1 = obj1[propertyName];
			               		var value2 = obj2[propertyName];
			                  	//比较函数接收两个参数，如果第一个参数应该位于第二个之前则返回一个负数，
			                    //如果两个参数相等 则返回 0，如果第一个参数应该位于第二个之后则返回一个正数。
			               		if(value1 < value2){
			               			return -1;
			               		}else if(value1 > value2){
			               			return 1;
			               		}else{
			               			return 0;
			               		}
			               	}
		               }
		              var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}]; 
		              data.sort(createComparisonFunction("name"));
		              console.log(data);//[{name: "Nicholas", age: 29},{name: "Zachary", age: 28}]; 
		              data.sort(createComparisonFunction("age"));
		              console.log(data);//[{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}];

                //5.5.4 函数内部属性
                  //在函数内部，有两个特殊的对象：arguments 和 this
                  //1.arguments是一个类数组对象，包含着传入函数中的所有参数。主要用途是保存函数参数
                  //  arguments对象还有一个名叫 callee的属性，该属性是一个指针，指向拥有这个 arguments 对象的函数
                  //  注意，当函数在严格模式下运行时，访问 arguments.callee 会导致错误
                  
                  //  以下代码函数的执行与函数名 factorial紧紧耦合在了一起
		                    function factorial(num){
		                    	if(num<=1){
		                    		return 1;
		                    	}else{
		                    		return num*factorial(num-1);
		                    	}
		                    }
		                    var trueFactorial = factorial;
		                    factorial = function(){
							   return 0; 
							};
							console.log(trueFactorial(5));//0
							console.log(factorial(5));//0
			     //  使用arguments.callee解除函数体内的代码与函数名的耦合状态
			            function factorial(num){
	                    	if(num<=1){
	                    		return 1;
	                    	}else{
	                    		return num*arguments.callee(num-1);
	                    	}
	                    }
			            var trueFactorial = factorial;
	                    factorial = function(){
						   return 0; 
						};
						console.log(trueFactorial(5));//120
						console.log(factorial(5));//0


			    //2. this引用的是函数据以执行的环境对象
			            //this对象是在运行时基于函数的执行环境绑定的
			            //函数的名字仅仅是一个包含指针的变量而已。
			            //因此，即使是在不同的环境中执行，全局的 sayColor()函数与 o.sayColor()指向的仍然是同一个函数。
			            //在严格模式下，未指定环境对象而调用函数，则 this 值不会转型为 window。
						       window.color = "red";
						       var o = {color:"blue"};
						       function sayColor(){
						       	  console.log(this.color);
						       }
							   sayColor();//非严格模式下，为red   严格模式下报错
							   o.sayColor = sayColor;
							   o.sayColor();//blue
				  
				
			    //5.5.5 函数属性和方法
			          //每个函数都包含两个属性：length 和 prototype。
			          //其中，length 属性表示函数希望接收的命名参数的个数。
			                   function f(a,b,c){
								 console.log(f.length);
							   }
							   f();//3
							   
							   
			      	  //每个函数都包含两个非继承而来的方法：apply()和 call()。
			          //这两个方法的用途都是在特定的作用域中调用函数，实际上等于设置函数体内 this对象的值。
			    
			          
			      //1.apply()方法接收两个参数：一个是在其中运行函数的作用域，另一个是参数数组。
			      //  其中，第二个参数可以是 Array 的实例，也可以是arguments 对象。
							      function sum(num1,num2){
							      	 return num1 + num2
							      }
							      function callSum1(num1,num2){
							      	 return sum.apply(this,arguments);
							      }
			      //2.对于 call()方法而言，第一个参数是 this 值没有变化，变化的是其余参数都直接传递给函数 。
			      //  在使用call()方法时，传递给函数的参数必须逐个列举出来
						          function sum(num1,num2){
							      	 return num1 + num2
							      }
							      function callSum1(num1,num2){
							      	 return sum.call(this,num1,num2);
							      }
                  //apply()和call()真正强大的地方是能够扩充函数赖以运行的作用域。
                  //使用 call()（或 apply()）来扩充作用域的最大好处，就是对象不需要与方法有任何耦合关系。
                                 window.color = "red";
								 var o = {color:"blue"};
								 function sayColor(){
									console.log(this.color);
									
								 }
								 sayColor.call(window);//red
								 sayColor.call(o);//blue

                  
                  //ES5 bind方法
                  //bind方法会创建一个函数的实例，其 this 值会被绑定到传给 bind()函数的值。
		                  window.color = "red";
		                  var o = {color:"blue"};
		                  function sayColor(){
		                  	console.log(this.color);
		                  }
		                  var objectSayColor = sayColor.bind(o);
		                  objectSayColor();
		</script>
	</body>
</html>
